#!/bin/sh

#功能：PHS系统appm系统组件，提供App安装卸载管理和安全一致性校验功能
#开发维护：Hihope Xi'an

# 初始化变量
APP_NAME=""
INSTALL_CMD=false
LIST_CMD=false
UNINSTALL_CMD=false
START_CMD=false
STOP_CMD=false
PACKAGE_PATH=""
PACKAGE_NAME=""
APP_VERSION=false
SHOW_VERSION=false
SHOW_HELP=false

PACKAGE_REAL_PATH=""
STATUS_FILE=""

# 初始化环境变量
ret=-1
PID=""

# 约定app管理数据库文件
APP_MANAGER=/userdata/data/appm.dat

# app管理库文件定义
#app_name,software_ver

## 以下定义系统变量

#App安装目录,在appm首次运行时创建
export INSTALL_PATH=/userdata/app

#App的数据和配置文件路径，在appm首次运行时创建
export APP_DATA=/userdata/data

#升级状态码	含义
CHECK_OK=101		#升级校验成功。
UPD_START=103		#升级开始。
UPD_ING=105			#升级中（带升级进度,例如，105&50）
UPD_END=107			#升级操作结束。
UPD_REBOOTED=108	#升级结束重启中
UPD_REBOOTING=109	#升级重启中。
UPD_OK=200			#升级成功
	
UPD_FAIL_MEMERR=501			#升级失败-内存池申请失败         
UPD_FAIL_CHECKERR=502		#升级失败-写后回读数据校验错误   
UPD_FAIL_BAKERR=503			#升级失败-备份数据校验失败       
UPD_FAIL_CACUERR=504		#升级失败-升级算法初始化错误     
UPD_FAIL_FSERR=505			#升级失败-文件系统错误           
UPD_FAIL_PACKERR=506		#升级失败-升级包格式错误         
UPD_FAIL_ROMSIZEERR=507		#升级失败-设备用于升级的内存不足 
UPD_FAIL_SRCCHKERR=508		#升级失败-升级源版本校验失败     
UPD_FAIL_AUTHERR=509		#升级失败-升级权限认证失败       
UPD_FAIL_FILENOTEXIST=510	#升级失败-升级包不存在           
UPD_FAIL_ROMERR=511			#升级失败-升级包存储空间不足     
UPD_FAIL_SUBDEVTIMEOUT=512	#升级失败-子设备升级超时         
UPD_FAIL_SUBDEVERR=513		#升级失败-子设备升级异常         
UPD_FAIL_OTHER=599			#升级失败-其他错误 


# 解析参数
while getopts ":n:p:ilsuSvVh" opt; do
  case ${opt} in
    n )
      APP_NAME=$OPTARG
      ;;
    i )
      INSTALL_CMD=true
      ;;
    p )
      PACKAGE_PATH=$OPTARG
      ;;
    l )
      LIST_CMD=true
      ;;
    s )
      START_CMD=true
      ;;
    u )
      UNINSTALL_CMD=true
      ;;
    S )
      STOP_CMD=true
      ;;
    v )
      SHOW_VERSION=true
      ;;
    V )
      APP_VERSION=true
      ;;
    h )
      SHOW_HELP=true
      ;;
    \? )
      echo "Invalid option: -$OPTARG" >&2
      SHOW_HELP=true
      ;;
    : )
      echo "Invalid option: -$OPTARG requires an argument" >&2
      SHOW_HELP=true
      ;;
  esac
done
shift $((OPTIND -1))

# 打印帮助信息
function print_help() {
  echo "Usage: appm [options]"
  echo "Options:"
  echo "  -n <name>          应用程序名"
  echo "  -i                 安装程序"
  echo "  -l                 打印安装程序信息"
  echo "  -u                 卸载程序"
  echo "  -s                 启动程序"
  echo "  -S                 停止程序"
  echo "  -p <path>          应用程序包路径"
  echo "  -v                 appm程序版本"
  echo "  -V                 获取安装的程序的版本"
  echo "  -h                 打印帮助信息"
}

function print_version() {
  echo "appm version: 1.0.0"
}

function check_userdata_size() {
	echo "正在检查用户分区空间..."
	#待实现
}

# 写状态接口
function log_status() {
    local status_code="$1"
	if [ -f "${STATUS_FILE}" ];then
		echo "$(date +"%Y%m%d%H%M%S%3N"):${status_code}" >> "${STATUS_FILE}"
	fi
}

skip_lines=2
# 表初始化
function init_table() {
	echo "#This file was generated by appms,Please don't eidt it.">${APP_MANAGER}
	echo "#App name,Software version">>${APP_MANAGER}
	return 0
}

# 新增一条信息
# 成功：0
# 失败：-1
function insert_info() {
	local new=$1

	if [[ -z "${new}" ]]; then
		return -1
	fi
	
	if [ ! -f ${APP_MANAGER} ]; then
		init_table
	fi
	
	item="${new%%,*}"
	
	while IFS=',' read -r name rest_of_line; do
		if [[ -n "${name}" ]]; then
			if [ "${item}" = "${name}" ]; then
				echo "Item已存在！"
				return -1
			fi
		fi
	done < "${APP_MANAGER}"
	
    if [ -f ${APP_MANAGER} ]; then
		echo ${new} >> ${APP_MANAGER}
	fi
	
    echo "信息已插入！"
	return 0
}

# 删除一条信息
# 成功：0
# 失败：-1
function delete_info() {
    local keyword=$1
	counter=0
	
	if [[ -z "${keyword}" ]]; then
		return -1
	fi
	
	if [ ! -f ${APP_MANAGER} ]; then
		return -1
	fi
	
	temp_file=$(mktemp)

	while IFS=',' read -r name rest_of_line; do
		if [ "${name:0:1}" = "#" ]; then
			echo "${name},${rest_of_line}" >> ${temp_file}
			continue
		fi
	
		if [[ -n "${name}" ]]; then
			if [ "${keyword}" = "${name}" ]; then
				echo "信息已删除！"
				echo "${name},${rest_of_line}"
				counter=$((counter + 1))
			else
				echo "${name},${rest_of_line}" >> ${temp_file}
			fi
		fi
	done < "${APP_MANAGER}"
	
	mv ${temp_file} ${APP_MANAGER}
	
	if [ ${counter} -eq 0 ];then
		echo "未找到包含 '${keyword}' 的信息！"
		return -1
	fi
	
	return 0
}

# 查询显示一条信息
# 查找成功：0
# 未找到：-1
function query_info() {
    local keyword=$1
	counter=0

	if [ -z "${keyword}" ]; then
		return -1
	fi
	
	if [ ! -f ${APP_MANAGER} ]; then
		return -1
	fi

	tail_output=$(mktemp)
	tail -n +$((skip_lines + 1)) "${APP_MANAGER}" > "${tail_output}"

	while IFS=',' read -r name rest_of_line; do
		if [[ -n "${name}" ]]; then
			if [ "${keyword}" = "${name}" ]; then
				echo "查询结果已显示！"
				echo "${name},${rest_of_line}"
				counter=$((counter + 1))
				break
			fi
		fi
	done < "${tail_output}"
	
	rm "${tail_output}"
	
	if [ ${counter} -eq 0 ];then
		echo "未找到相关记录。"
		return -1
	fi
	
	return 0
}

# 列出所有app的状态信息
# 成功：0
# 失败：-1
function show_all_info() {
    if [[ -s "${APP_MANAGER}" ]]; then
		while IFS= read -r line; do
			if [[ "${line}" != \#* ]]; then
				name="${line%%,*}"
			fi
			show_one_info ${name}
		done < "${APP_MANAGER}"
    else
        echo "未找到系统上安装的app。"
    fi
	return 0
}

# 列出系统中某个app的状态信息
# 成功：0
# 失败：返回-1
function show_one_info() {
	local app_name=$1
	local status=""
	local pid=""
	if [[ -n "${app_name}" ]]; then
		get_pid ${app_name}
		
		if [ $? -ne 0 ]; then
			status="STOPPED"
		else
			pid=${PID}
			status="RUNNING"
		fi

		if [[ -n "${pid}" ]]; then
			printf "%-32s %-16s %-16s\n" "${app_name}" "${status}" "pid=${pid}"
		else
			printf "%-32s %-16s\n" "${app_name}" "${status}"
		fi
	fi
	return 0
}

# 解析PACKAGE_NAME
function get_package_name() {
	local path="$1"
	PACKAGE_NAME=${path##*/}
	return 0
}

# 解析PACKAGE_REAL_PATH
function get_package_real_path() {
	local path="$1"

	case "$path" in
	  */*)
		PACKAGE_REAL_PATH="${path%/*}"
		;;
	  *)
		PACKAGE_REAL_PATH=$(pwd)
		;;
	esac
	
	if [ -n "${PACKAGE_REAL_PATH}" ];then
		if [ "${PACKAGE_REAL_PATH}" = "." ];then
			PACKAGE_REAL_PATH=$(pwd)
		fi
	fi
	
	return 0
}

# 解析APP_NAME
function get_app_name() {
	local path="$1"
	get_package_name ${path}
	if [ $? -eq 0 ]; then
		if [ -z "${PACKAGE_NAME}" ]; then
			return -1
		fi
		APP_NAME=${PACKAGE_NAME%%_*}
		return 0
	fi
	return -1
}

# 获取pid
function get_all_pids() {
	ps_output=$(ps -ef)
	pids=()
	while IFS= read -r line; do
		pid=$(echo "$line" | awk '{print $2}')
		if [[ -n "${pid}" ]]; then
			pids+=("${pid}")
		fi
	done <<< "${ps_output}"
	
	for pid in "${pids[@]}"; do
		echo "${pid}"
	done
	retrun 0
}

# 获取某个应用的pid
# 成功：0
# 失败：-1
function get_pid() {
	local app_name=$1
	ps_output=$(ps -ef | grep -w ${app_name} | grep -v grep | grep -v appm)
	
	if [ -z "${ps_output}" ]; then
		return -1
	fi
	
	PID=$(echo "${ps_output}" | awk '{print $2}')
	return 0
}

# 检查某个应用是否已安装
# 已安装：0
# 未安装：-1
function is_installed() {
	local app_name=$1
	query_info ${app_name}
	
	if [ $? -eq 0 ]; then
		return 0
	fi
	
	return -1
}

# 校验包
# 成功：0
# 失败：-1
function sha256_check() {
	local package_file=$1
	# 初始化配置文件
	if [ -n ${PACKAGE_REAL_PATH} ];then
	 if [ -n ${APP_NAME} ];then
		CONFIG_FILE=${PACKAGE_REAL_PATH}/install.json
	 fi
	fi

	if [ ! -f ${CONFIG_FILE} ];then
		echo "未找到配置文件！"
		return -1
	fi
	
	if [ ! -f ${package_file} ];then
		echo "未找到待验证文件！"
		return -1
	fi
	
	sha256=$(busybox sha256sum ${package_file})
	hash_value=$(echo "${sha256}" | cut -d' ' -f1)
	 
	while IFS= read -r line
	do
		trimmed_line="${line#"${line%%[![:space:]]*}"}"
		trimmed_line="${trimmed_line%"${trimmed_line##*[![:space:]]}"}"
	 
		if echo "$trimmed_line" | grep -q sha256; then
			value=$(echo "$trimmed_line" | sed -E 's/.*"sha256Hex":"([^"]*)".*/\1/')
			sha_list+=("${value}")
		fi
	done < ${CONFIG_FILE}


	for value in "${sha_list[@]}"; do
		if [ "${hash_value}" = "${value}" ];then
			echo "SHA256校验成功。"
			log_status ${CHECK_OK}
			return 0
		fi
	done
	
	return -1
}

# 平台化需求修改，归一化所有成功为install success
# 2025-03-07
function print_success() {
	echo "${APP_NAME} install success"
}

# 平台化需求修改，归一化所有成功为install success
# 2025-03-07
function print_failed() {
	echo "${APP_NAME} install failed"
}
	
# 传入apps包的PACKAGE_PATH，使用-i指令安装
# 成功：install.sh的返回
# 失败：-1
function perform_install() {
  # 安装所需空间检查，待完善
  check_userdata_size

  # 检查缓存文件夹
  if [ ! -e ${INSTALL_TEMP} ]; then
	mkdir -p ${INSTALL_TEMP}
  fi
  
  # 解析path
  get_app_name ${PACKAGE_PATH}
  get_package_real_path ${PACKAGE_PATH}
  
  # 创建缓存  
  if [ -n ${PACKAGE_REAL_PATH} ];then
	 INSTALL_TEMP=${PACKAGE_REAL_PATH}/.temp
  fi
  
  echo TEmp文件夹
  echo ${INSTALL_TEMP}
  
  if [ ! -e ${INSTALL_TEMP} ]; then
	mkdir -p ${INSTALL_TEMP}
  fi
  
  # 初始化状态文件
  if [ -n ${PACKAGE_REAL_PATH} ];then
	 if [ -n ${APP_NAME} ];then
		STATUS_FILE=${PACKAGE_REAL_PATH}/${PACKAGE_NAME}_upgradeStatus.ini
	 fi
  fi

  if [ ! -f ${STATUS_FILE} ];then
  	echo "未找到状态上报文件"
  fi
  
  if [ -f ${STATUS_FILE} ];then
  	#初始化清空STATUS_FILE
  	cat /dev/null > ${STATUS_FILE}
  fi
	  
  echo "正在安装软件包: ${PACKAGE_REAL_PATH}/${PACKAGE_NAME}"
  
  # Sha256检查
  sha256_check ${PACKAGE_REAL_PATH}/${PACKAGE_NAME}
  ret=$?
  if [ ${ret} -ne 0 ]; then
  	echo "ERROR: Sha256检查."
	print_failed
  	return ${ret}
  fi
  
  # 检查是否已安装
  updating=0
  is_installed ${APP_NAME}
  if [ $? -eq 0 ]; then
	updating=1
  fi
  
  log_status ${UPD_START}

  if [ -f ${PACKAGE_REAL_PATH}/${PACKAGE_NAME} ]; then
    busybox gzip -t ${PACKAGE_REAL_PATH}/${PACKAGE_NAME}
	ret=$?
	if [ ${ret} -ne 0 ]; then
		echo "ERROR: 安装包文件错误."
		print_failed
		return ${ret}
	fi
	
	echo "正在解压安装包..."
	tar -zxf ${PACKAGE_REAL_PATH}/${PACKAGE_NAME} -C ${INSTALL_TEMP}
	ret=$?
	if [ ${ret} -ne 0 ]; then
		echo "ERROR: 解压安装包错误."
		print_failed
		# 清理临时安装文件
		rm -rf ${INSTALL_TEMP}
		return ${ret}
	fi
	
	log_status "${UPD_ING}&20"
	
	cd ${INSTALL_TEMP}
	if [ -f "install.sh" ]; then
		./install.sh
		ret=$?
	fi
	
	log_status "${UPD_ING}&50"
	
	# 安装成功，更新库文件
	app_version="null"
	if [ -f "install.sh" ]; then
		app_version=$(./get_version.sh)
	fi
	
	log_status "${UPD_ING}&80"
	
	if [ ${ret} -eq 0 ]; then
		echo "正在更新管理文件..."

		if [ ${updating} -eq 1 ]; then
			delete_info ${APP_NAME}
		fi
		
		insert_info ${APP_NAME},${app_version}
		if [ $? -ne 0 ]; then
			if [ -f "uninstall.sh" ]; then
				./uninstall.sh
				echo "安装失败!"
				print_failed
				log_status ${UPD_FAIL_OTHER}
				
				# 清理临时安装文件
				rm -rf ${INSTALL_TEMP}
				return -1
			fi
		fi
		
		#至此安装成功
		echo "安装成功!"
		print_success
		log_status "${UPD_ING}&100"
		log_status ${UPD_END}
		log_status ${UPD_OK}
		
		# 清理临时安装文件
		rm -rf ${INSTALL_TEMP}
		# 返回error code
		return ${ret}		
	fi
  else
  	echo "ERROR: 安装包未找到."
	print_failed
	log_status ${UPD_FAIL_FILENOTEXIST}
	ret=${UPD_FAIL_FILENOTEXIST}
  	return ${ret}
  fi
}

# 卸载一个应用
# 成功：uninstall.sh的返回
# 失败：-1
function perform_uninstall() {
	echo "正在卸载应用: ${APP_NAME}"
	if [ ! -e "/userdata/app/${APP_NAME}" ]; then
		echo "找不到 '${APP_NAME}' 的安装路径"
		# 更新管理库文件
		delete_info ${APP_NAME}
		return -1
	fi
	
	cd /userdata/app/${APP_NAME}
	if [ -f "uninstall.sh" ]; then
		./uninstall.sh
		ret=$?
	fi

	if [ ${ret} -eq 0 ]; then
		echo "${APP_NAME} 卸载成功！"
		# 更新管理库文件
		delete_info ${APP_NAME}
	fi
	return ${ret}
}

# 启动一个应用
# 成功：0
# 失败：-1
function perform_start() {
	echo "正在启动应用: ${APP_NAME}"
	if [ ! -e "/userdata/app/${APP_NAME}" ]; then
		echo "找不到 '${APP_NAME}' 安装路径"
		return -1
	fi

	if [ ! -f "/userdata/app/${APP_NAME}/${APP_NAME}" ]; then
		echo "找不到 '${APP_NAME}'"
		return -1
	fi
	
	cd /userdata/app/${APP_NAME}
	./${APP_NAME} &
	echo "'${APP_NAME}' 已启动"
	return 0
}

# 停止一个应用
# 成功：0
# 失败：-1
function perform_stop() {
	echo "正在停止应用: ${APP_NAME}"
	get_pid ${APP_NAME}
	
	if [ $? -ne 0 ]; then
		echo "找不到'${APP_NAME}'的PID"
		return $?
	fi
	
	if [ -n "${PID}" ]; then
		echo "正在结束 '${APP_NAME}'"
		kill ${PID}
		if [ $? -eq 0 ]; then
			echo "结束 '${APP_NAME}' 成功。"
			return 0
		fi
	fi
	
	return -1
}

# 查询系统里所有安装的app版本
# 成功：0
# 失败：-1
function perform_show_appv() {
    if [[ -s "${APP_MANAGER}" ]]; then
		while IFS= read -r line; do
			if [[ "${line}" != \#* ]]; then
				name="${line%%,*}"
				IFS=',' read -r _ softver <<< "$line"
				printf "%-32s %-16s\n" "${name}" "software version：${softver}"
			fi
		done < "${APP_MANAGER}"
    else
        echo "未找到系统上安装的app。"
		return -1
    fi
	
	return 0
}


# 初始化全局目录
if [ ! -e ${INSTALL_PATH} ]; then
	mkdir -p ${INSTALL_PATH}
fi

if [ ! -e ${APP_DATA} ]; then
	mkdir -p ${APP_DATA}
fi


# 解析入参
if [ "$LIST_CMD" = true ]; then
  if [ -z "${APP_NAME}" ]; then
    show_all_info
  else
    show_one_info ${APP_NAME}
	exit $?
  fi
elif [ "${INSTALL_CMD}" = true ]; then
  if [ -z "${PACKAGE_PATH}" ]; then
    echo "Error: 安装软件包时，请提供软件包路径..." >&2
    SHOW_HELP=true
  else
    perform_install
	exit $?
  fi
elif [ "${UNINSTALL_CMD}" = true ]; then
  if [ -z "${APP_NAME}" ]; then
    echo "Error: 卸载软件包，请提供应用名" >&2
    SHOW_HELP=true
  else
    perform_uninstall
	exit $?
  fi
elif [ "${START_CMD}" = true ]; then
  if [ -z "${APP_NAME}" ]; then
    echo "Error: 启动应用，请提供应用名" >&2
    SHOW_HELP=true
  else
    perform_start
	exit $?
  fi
elif [ "${STOP_CMD}" = true ]; then
  if [ -z "${APP_NAME}" ]; then
    echo "Error: 停止应用，请提供应用名" >&2
    SHOW_HELP=true
  else
    perform_stop
	exit $?
  fi
elif [ "${APP_VERSION}" = true ]; then
   perform_show_appv
   exit $?
elif [ "${SHOW_VERSION}" = true ]; then
  print_version
else
  echo "无效参数."
  SHOW_HELP=true
fi

# 如果需要打印帮助信息，则打印
if [ "${SHOW_HELP}" = true ]; then
  print_help
fi
